# python学习 至此开始
# 学习日期: 2023/8/31 16:11
# 学习者: 任磊


"""
账户文件默认格式为： 用户名验证码(64) + 空格 + 密码验证码(64)
验证码为对应的数据的sm3哈希值
此版本尝试进行了独立页面的开发
此版本中存在待完善项: 通信功能的密钥分发模式下，对于通信存在风险的情况尚未作出更改(但在同一套系统下基本不会出现此情况)
"""

# 导入需要的socket,sys等其他的库
from socket import *
import os
import time
import pyautogui
import random
import string
import SystemSign
import SystemVerify
from tkinter import *
from tkinter import messagebox
import threading
# 导入所需的三种国密算法以及其他必要的库
from pysm4 import *
from gmssl import func, sm2, sm3
from base64 import b64encode, b64decode


# 定义密码库调用类
class Crypto_program(object):
    def __init__(self):
        super().__init__()
        pass

    # 随机生成SM2公私钥对的目标文件
    random_SM2_key_born_file = 'python E:\密码学小学期20230821\python_socket通信\国密SM2随机生成密钥文件.py'
    # 公私钥密钥对
    private_key = None
    public_key = None
    sm2_crypt = None
    # SM2密钥生成模式: 1 随机生成(默认)，2 使用用户自己的公钥 ，其他 指定公钥
    sm2_create_mode = 1

    # 对称密钥基础强度检测，合法性检测的方法(long: 限制的最大长度)
    @staticmethod
    def test_key_strong(key, long):
        pass_flag1 = False
        pass_flag2 = False
        # 判断传入数据的合法性
        if type(long) != int:
            long = 8
        else:
            if int(long) < 8:
                long = 8
            else:
                long = int(long)
        # 检测密钥长度
        key_long = key.encode()
        print(len(key_long))
        if 8 <= len(key_long) <= long:
            # 检测是否含有数字
            for word1 in key:
                if word1.isalnum():
                    pass_flag1 = True
                    break
            # 检测是否含有字母
            for word2 in key:
                if word2.isalpha():
                    pass_flag2 = True
                    break
            if pass_flag1 is True and pass_flag2 is True:
                return True
            else:
                return False
        else:
            return False

    # 随机生成SM2公私钥对的方法
    @staticmethod
    def sm2_key_random_born():
        # 运行随机密钥生成文件
        os.system(Crypto_program.random_SM2_key_born_file)

    # 检测生成的SM2公私对是否匹配的方法
    @staticmethod
    def test_sm2_key():
        # 以只读模式打开公私钥的文件
        file1 = open("E:\密码学小学期20230821\python_socket通信\pk_B.txt", "r")
        publickey = file1.read()
        file1.close()
        file2 = open("E:\密码学小学期20230821\python_socket通信\sk_B.txt", "r")
        privatekey = file2.read()
        file2.close()
        # 检测签名与验签
        # 利用读取的公私钥对生成sm2对象
        sm2_crypto = sm2.CryptSM2(public_key=publickey, private_key=privatekey)
        # 调用随机密钥生成的结果为检测对象
        Crypto_program.random_key_born()
        test_data = Connection_program.connect_key_sm4
        # 哈希
        hash_data = sm3.sm3_hash(func.bytes_to_list(test_data.encode()))
        # 签名
        sign_data = sm2_crypto.sign(hash_data.encode(), str(len(test_data)))
        # 验签
        if sm2_crypto.verify(sign_data, hash_data.encode()):
            return True
        else:
            return False

    # 生成SM2加解密对象的方法
    @staticmethod
    def create_sm2(flag):
        # 判断输入数据合法性
        if type(flag) == int:
            Crypto_program.sm2_create_mode = flag
        else:
            pass
        # 判断sm2加解密对象生成模式
        if Crypto_program.sm2_create_mode == 1:
            # 随机生成
            # 调用SM2密钥随机生成的方法
            Crypto_program.sm2_key_random_born()
            # 打开随机生成的公私钥文件
            sf = open("E:\密码学小学期20230821\python_socket通信\sk_B.txt")
            Crypto_program.private_key = (sf.read())
            f = open("E:\密码学小学期20230821\python_socket通信\pk_B.txt")
            Crypto_program.public_key = (f.read())
            sf.close()
            f.close()
        elif Crypto_program.sm2_create_mode == 2:
            # 使用用户自己的SM2公私钥对
            Crypto_program.public_key = File_program.user_sm2_public_key
            Crypto_program.private_key = File_program.user_sm2_private_key
        else:
            # 指定公钥
            # 调用随机密钥生成的方法
            Crypto_program.private_key = ""
        # 创建SM2加解密对象
        Crypto_program.sm2_crypt = sm2.CryptSM2(public_key=Crypto_program.public_key, private_key=Crypto_program.private_key)

    # 国密SM2加密的方法
    @staticmethod
    def sm2_encrypt(info):
        encode_info = Crypto_program.sm2_crypt.encrypt(info.encode(encoding="utf-8"))
        encode_info = b64encode(encode_info).decode()  # 将二进制bytes通过base64编码
        return encode_info

    # 国密SM2解密的方法
    @staticmethod
    def sm2_decrypt(info):
        decode_info = b64decode(info.encode())  # 通过base64解码成二进制bytes
        decode_info = Crypto_program.sm2_crypt.decrypt(decode_info).decode(encoding="utf-8")
        return decode_info

    # 随机生成对称密钥的方法(若输入非法，则默认输出 16 字节长度 sm4)
    @staticmethod
    def random_key_born():
        # 判断输入是否非法
        if type(Connection_program.random_key_long) == int:
            if int(Connection_program.random_key_long) > 0:
                pass
            else:
                Connection_program.random_key_long = - int(Connection_program.random_key_long)
        else:
            Connection_program.connect_key_sm4 = 16
        # 生成密钥
        words = string.ascii_lowercase + string.ascii_uppercase + string.digits + string.punctuation
        # 根据长度随机采样几个字符，得到一个列表
        chosen = random.sample(words, Connection_program.random_key_long)
        # 将列表的每个元素，拼接成一个大字符串
        # 赋值密钥
        Connection_program.connect_key_sm4 = "".join(chosen)

    # 创建接收公钥创建SM2验签的方法(密钥分发模式下)
    @staticmethod
    def create_other_sm2(puk):
        sm2_crypto = sm2.CryptSM2(public_key=puk, private_key="")
        return sm2_crypto

    # 创建用户自己的SM2公私钥加解密的方法(密钥分发模式下)
    @staticmethod
    def create_my_sm2():
        sm2_crypto = sm2.CryptSM2(public_key=File_program.user_sm2_public_key, private_key=File_program.user_sm2_private_key)
        return sm2_crypto

    # sm2使用仅公钥加密的方法
    @staticmethod
    def sm2_publickey_encry(sm2_crypto, info):
        encode_info = sm2_crypto.encrypt(info.encode(encoding="utf-8"))
        encode_info = b64encode(encode_info).decode()  # 将二进制bytes通过base64编码
        return encode_info

    # sm2使用私钥解密的方法
    @staticmethod
    def sm2_privatekey_decry(sm2_crypto, info):
        decode_info = b64decode(info.encode())  # 通过base64解码成二进制bytes
        decode_info = sm2_crypto.decrypt(decode_info).decode(encoding="utf-8")
        return decode_info


# 定义通信连接类
class Connection_program(object):
    def __init__(self):
        super().__init__()
        pass

    # 连接状态
    connect_flag = False
    # 通信密钥相关参数
    connect_key_sm4 = ""
    random_key_long = 16
    # 临时会话密钥
    time_sm4_key = ""
    # 通信其它参数
    Max_data = 8192
    # 时间记录
    connect_time = ""
    # 临时记录列表
    time_record_data = []
    # 临时会话记录变量
    start_time = ""
    end_time = ""
    connect_time_long = ""
    # 密钥分发模式记录变量
    time_record_key_division_list = []
    # 会话模式
    communication_frame = None
    communication_text = None
    communication_send_data = None
    send_data_flag = False
    send_data = ""
    # 密钥分发模式
    key_distribution_frame = None
    key_distribution_text = None
    target_key = None
    target_key_data = ""
    send_target_key_button = None
    key_distribution_exit_flag = True

    # 设置通信所需的IP配置的函数
    @staticmethod
    def get_host_ip():
        s = socket(AF_INET, SOCK_DGRAM)
        try:
            s.connect(('8.8.8.8', 80))  # 114.114.114.114也是dns地址
            ip = s.getsockname()[0]
        finally:
            s.close()
        return ip

    # 解码并解密消息的方法(会话模式下)
    @staticmethod
    def message_decode_decry_recv(dataSocket):
        # 接收消息
        text = dataSocket.recv(Connection_program.Max_data)
        # 解码
        text = text.decode("utf-8")
        # 使用临时会话密钥解密消息
        text = decrypt_ecb(text, Connection_program.time_sm4_key)
        # 判断消息内容
        if text == "结束" or text == "exit":
            print("\033[91m 对方已结束通信！ \033[0m")
            # 改变通信状态
            Connection_program.connect_flag = False
        else:
            # 展示消息
            print("收到消息:\033[34m{}\033[0m".format(text))
        # 将消息添加到记录列表中
        Connection_program.time_record_data.append(text + "\n")

    # 加密编码并发送消息的方法(会话模式下)
    @staticmethod
    def message_encry_encode_send(dataSocket):
        # 等待输入消息
        text = input("")
        # 判断输入内容
        if text == "结束" or text == "exit":
            print("0\33[91m 您已主动结束通信！ \033[0m")
            # 改变通信状态
            Connection_program.connect_flag = False
        else:
            pass
        # 将明文消息添加到记录列表中
        Connection_program.time_record_data.append("(" + text + ")" + "\n")
        # 消息加密
        text = encrypt_ecb(text, Connection_program.time_sm4_key)
        # 消息编码后发送
        dataSocket.send(text.encode())

    # 会话模式的初始化多线程
    @staticmethod
    def init_communication():  # 当前多线程成功
        # 创建通信界面多线程
        th1 = threading.Thread(target=Connection_program.create_communication_frame)
        th1.start()

    # 会话模式界面的创建方法
    @staticmethod
    def create_communication_frame():
        Connection_program.communication_send_data = StringVar()
        # 创建界面
        Connection_program.communication_frame = Frame(root_window, padx=40, pady=20)
        Connection_program.communication_frame.place(anchor="center", relx=0.5, rely=0.5, )
        # 创建显示的消息框区域
        Connection_program.communication_text = Text(Connection_program.communication_frame, width=100, height=40,)
        Connection_program.communication_text.pack()
        # 将滚动条设置在容器右边并填充全部y轴
        Scrollbar(Connection_program.communication_frame, command=Connection_program.communication_text.yview).pack(side="right", fill="y")
        # 启动通信的按钮
        Button(Connection_program.communication_frame, text="开启通信", font=("黑体", 15), fg="blue", command=Connection_program.start_connect).pack(side="left")
        # 输入数据的文本框
        Entry(Connection_program.communication_frame, textvariable=Connection_program.communication_send_data, font=("黑体", 12), width=40, fg="green").pack(side="left", padx=10, pady=10)
        # 发送按钮
        Button(Connection_program.communication_frame, text="发送", font=("黑体", 15), fg="blue", command=Connection_program.add_send_data_to_text).pack(side="left")
        # 退出按钮
        Button(Connection_program.communication_frame, text="退出", font=("黑体", 15), fg="red", command=Connection_program.communication_mode_to_communication_interface).pack(side="right")

    # 启动通信的方法
    @staticmethod
    def start_connect():
        # 判断当前通信状态
        if Connection_program.connect_flag is False:
            # 创建通信多线程
            th2 = threading.Thread(target=Connection_program.communication_connect)
            th2.start()

    # 将发送的数据显示在消息框区域的方法
    @staticmethod
    def add_send_data_to_text():
        data = Connection_program.communication_send_data.get()
        # 判断是否可以发送消息
        if Connection_program.send_data_flag is True and data != "":
            Connection_program.communication_text.config(state="normal")
            Connection_program.send_data = data
            # 将发送的消息粘贴至消息框中
            Connection_program.communication_text.insert("end", "发送: " + data + "\n")
            Connection_program.communication_send_data.set("")
            Connection_program.send_data_flag = False
        else:
            pass

    # 由会话模式返回通信界面的方法
    @staticmethod
    def communication_mode_to_communication_interface():
        # 判断通信状况
        if Connection_program.connect_flag is False:
            Connection_program.communication_frame.destroy()
            # 初始化各项变量，为下一次通信做准备
            Connection_program.communication_frame = None
            Connection_program.communication_text = None
            Connection_program.communication_send_data = None
            Connection_program.send_data_flag = False
            # 清除窗口内组件
            Main_program.clear_frame(root_window)
            # 调用通信界面的方法
            Main_program.communication_interface()

    # 启动会话通信连接的方法
    @staticmethod
    def communication_connect():
        # 改变通信状态
        Connection_program.connect_flag = True
        # 调用插件会话界面的方法
        Connection_program.create_communication_frame()
        # 通信部分
        IP = Connection_program.get_host_ip()  # IPV4
        PORT = 8000  # 端口
        Max_data = 1024  # 缓冲区大小
        # 设置网络层协议，传输层协议
        listenSocket = socket(AF_INET, SOCK_STREAM)  # IP协议，TCP
        listenSocket.bind((IP, PORT))  # 绑定地址和端口
        # 设置监听
        listenSocket.listen(2)  # 设置2个监听口
        # ------------------------------------------------------------------------------------------加入数据到文本框中
        Connection_program.communication_text.insert("end", f'启动成功，正在端口{PORT}等待建立连接...' + "\n")
        # 设置连接
        dataSocket, addr = listenSocket.accept()  # 设置接收
        # ------------------------------------------------------------------------------------------加入数据到文本框中
        Connection_program.communication_text.insert("end", f'连接成功，来自{addr}地址的连接已就绪，等待对方发送公钥...' + "\n")
        try:
            # 接收公钥
            PUK = dataSocket.recv(Max_data)
            public_key = PUK.decode('UTF-8')
            # print(f"已接收到公钥:{public_key}")
            # ------------------------------------------------------------------------------------------加入数据到文本框中
            Connection_program.communication_text.insert("end", f"已接收到公钥:{public_key}" + "\n")
            # 接收公钥并生成sm2加解密对象
            Crypto_program.public_key = public_key
            Crypto_program.create_sm2(3)
            # 调用密钥随机生成的函数
            Crypto_program.random_key_born()
            Connection_program.time_sm4_key = Connection_program.connect_key_sm4
            # print("已随机生成通信密钥:{}".format(Connection_program.time_sm4_key))
            # ------------------------------------------------------------------------------------------加入数据到文本框中
            Connection_program.communication_text.insert("end", "已随机生成通信密钥:{}".format(Connection_program.time_sm4_key) + "\n")
            # 发送处理后的通信密钥
            encry_key = Crypto_program.sm2_encrypt(Connection_program.time_sm4_key)  # 使用接收的公钥对通信密钥进行加密
            dataSocket.send(encry_key.encode())
            # print("已发送使用对方公钥加密处理后的通信密钥数据:{}".format(encry_key))
            # ------------------------------------------------------------------------------------------加入数据到文本框中
            Connection_program.communication_text.insert("end", "已发送使用对方公钥加密处理后的通信密钥数据:{}".format(encry_key) + "\n")
            # 接收用于验证通信密钥的消息
            Crypto_program.random_key_born()
            insure_data = Connection_program.connect_key_sm4  # 通信密钥验证消息
            # ------------------------------------------------------------------------------------------加入数据到文本框中
            Connection_program.communication_text.insert("end", "已生成随机验证消息:{}".format(insure_data) + "\n")
            en_insure_data = encrypt_ecb(insure_data, Connection_program.time_sm4_key)  # 使用通信密钥加密验证消息
            dataSocket.send(en_insure_data.encode())  # 发送验证消息
            # print('已发送加密后的验证消息:{}'.format(en_insure_data))
            # ------------------------------------------------------------------------------------------加入数据到文本框中
            Connection_program.communication_text.insert("end", '已发送加密后的验证消息:{}'.format(en_insure_data) + "\n")
            # 接收签名消息
            recv_word = dataSocket.recv(Max_data).decode()  # 接收验证消息
            # print("接收到加密的签名消息:{}".format(recv_word))
            # ------------------------------------------------------------------------------------------加入数据到文本框中
            Connection_program.communication_text.insert("end", "接收到加密的签名消息:{}".format(recv_word) + "\n")
            # 解密
            recv_word = decrypt_ecb(recv_word, Connection_program.time_sm4_key)
            # print("解密后为:{}".format(recv_word))
            # ------------------------------------------------------------------------------------------加入数据到文本框中
            Connection_program.communication_text.insert("end", "解密后为:{}".format(recv_word) + "\n")
            # 将自己的验证消息进行哈希
            insure_data_hash = sm3.sm3_hash(func.bytes_to_list(insure_data.encode()))
            # print("原验证消息哈希后为:{}".format(insure_data_hash))
            # print("正在验证消息...")
            # ------------------------------------------------------------------------------------------加入数据到文本框中
            Connection_program.communication_text.insert("end", "原验证消息哈希后为:{}".format(insure_data_hash) + "\n")
            Connection_program.communication_text.insert("end", "正在验证消息..." + "\n")
            sm2_crypto = sm2.CryptSM2(private_key="", public_key=public_key)
            if sm2_crypto.verify(recv_word, insure_data_hash.encode()):  # 验证验签名消息，以确定对方是否收到了正确的通信密钥
                # print('\033[32m对方已正确接收到通信密钥，开始通信吧\033[0m')
                # ------------------------------------------------------------------------------------------加入数据到文本框中
                Connection_program.communication_text.insert("end", "对方已正确接收到通信密钥，开始通信吧" + "\n")
                dataSocket.send(encrypt_ecb("OK", Connection_program.time_sm4_key).encode())
            else:  # 未通过消息验证，通信结束
                # print('\033[91m对方未正确接收到通信密钥，通信结束\033[0m')
                # ------------------------------------------------------------------------------------------加入数据到文本框中
                Connection_program.communication_text.insert("end", "对方未正确接收到通信密钥，通信结束" + "\n")
                dataSocket.send(encrypt_ecb("NO", Connection_program.time_sm4_key).encode())
                Connection_program.connect_flag = False
            if Connection_program.connect_flag is True:
                # print("正在等待对方发送消息")
                # ------------------------------------------------------------------------------------------加入数据到文本框中
                Connection_program.communication_text.insert("end", "正在等待对方发送消息" + "\n")
                # 组合多线程
                # th1 = threading.Thread(target=Connection_program.message_encry_encode_send, args=(dataSocket, ))
                # th2 = threading.Thread(target=Connection_program.message_decode_decry_recv, args=(dataSocket, ))
                # 启用多线程
                # th1.start()
                # th2.start()
                # 记录通信时的日期数据
                Connection_program.connect_time = time.strftime('%Y-%m-%d %H:%M:%S')
                # 记录会话开始时间
                Connection_program.start_time = time.time()
                # 接收对方用户名
                other_name = dataSocket.recv(Max_data).decode()
                other_name = decrypt_ecb(other_name, Connection_program.time_sm4_key)
                # 发送自己用户名
                dataSocket.send(encrypt_ecb(Main_program.user_name, Connection_program.time_sm4_key).encode())
                while True:
                    # 接收消息
                    text = dataSocket.recv(Connection_program.Max_data)
                    # 解码
                    text = text.decode("utf-8")
                    # 使用临时会话密钥解密消息
                    text = decrypt_ecb(text, Connection_program.time_sm4_key)
                    # 判断消息内容
                    if text == "结束" or text == "exit":
                        # print("\033[91m 对方已结束通信！ \033[0m")
                        # ------------------------------------------------------------------------------------------加入数据到文本框中
                        Connection_program.communication_text.insert("end", "对方已结束通信！" + "\n")
                        # 将消息添加到记录列表中
                        Connection_program.time_record_data.append(text + "\n")
                        # 改变通信状态
                        Connection_program.connect_flag = False
                        break
                    else:
                        # 展示消息
                        # print("收到消息:\033[34m{}\033[0m".format(text))
                        # ------------------------------------------------------------------------------------------加入数据到文本框中
                        Connection_program.communication_text.insert("end", "{}: {}".format(other_name, text) + "\n")
                        # 改变相关参数
                        Connection_program.send_data_flag = True
                        # 将消息添加到记录列表中
                        Connection_program.time_record_data.append(other_name + ": " + text + "\n")
                    # 等待输入消息
                    while True:
                        if Connection_program.send_data != "":
                            text = Connection_program.send_data
                            Connection_program.send_data = ""
                            Connection_program.send_data_send_flag = False
                            break
                    # 判断输入内容
                    if text == "结束" or text == "exit":
                        # print("\033[91m 您已主动结束通信！ \033[0m")
                        # ------------------------------------------------------------------------------------------加入数据到文本框中
                        Connection_program.communication_text.insert("end", "您已主动结束通信！" + "\n")
                        # 消息编码后发送
                        dataSocket.send(encrypt_ecb(text, Connection_program.time_sm4_key).encode())
                        # 将明文消息添加到记录列表中
                        Connection_program.time_record_data.append("(" + text + ")" + "\n")
                        # 改变通信状态
                        Connection_program.connect_flag = False
                        break
                    else:
                        # 将明文消息添加到记录列表中
                        Connection_program.time_record_data.append("(" + text + ")" + "\n")
                        # 消息加密
                        text = encrypt_ecb(text, Connection_program.time_sm4_key)
                        # 消息编码后发送
                        dataSocket.send(text.encode())
                # 记录会话结束时间
                Connection_program.end_time = time.time()
                # 计算会话时长
                Connection_program.connect_time_long = Connection_program.end_time - Connection_program.start_time
                # 将此次会话记录进行保存
                File_program.file_batch_encrypto(1)
        finally:
            listenSocket.close()  # 关闭监听
            dataSocket.close()  # 关闭连接
            Connection_program.communication_text.insert("end", "会话结束！" + "\n")

    # 启动密钥分发模式多线程的方法
    @staticmethod
    def init_key_distribution():
        th1 = threading.Thread(target=Connection_program.create_key_distribution)
        # 启用多线程
        th1.start()

    # 创建密钥分发模式界面的方法
    @staticmethod
    def create_key_distribution():
        # 初始化变量数据
        Connection_program.target_key = StringVar()
        # 创建密钥分发界面
        Connection_program.key_distribution_frame = Frame(root_window, padx=40, pady=60)
        Connection_program.key_distribution_frame.place(anchor="center", relx=0.5, rely=0.5, )
        # 创建显示的消息框区域
        Connection_program.key_distribution_text = Text(Connection_program.key_distribution_frame, width=100, height=40,)
        Connection_program.key_distribution_text.pack()
        # 将滚动条设置在容器右边并填充全部y轴
        Scrollbar(Connection_program.key_distribution_frame, command=Connection_program.key_distribution_text.yview).pack(side="right", fill="y")
        # 输入数据的文本框
        Entry(Connection_program.key_distribution_frame, font=("黑体", 12), width=40, fg="green", textvariable=Connection_program.target_key).pack(side="left", padx=10, pady=10)
        # 发送按钮
        Connection_program.send_target_key_button = Button(Connection_program.key_distribution_frame, text="开始发送", font=("黑体", 15), fg="blue", command=Connection_program.send_target_key)
        Connection_program.send_target_key_button.pack(side="left")
        # 退出按钮
        Button(Connection_program.key_distribution_frame, text="退出", font=("黑体", 15), fg="red", command=Connection_program.key_distribution_mode_to_communication_interface).pack(side="right")
        # 提示语
        # Label(Connection_program.key_distribution_frame, text="输入密码后点击开始发送以发送密钥！", font=("黑体", 15), fg="red",).pack()

    # 将密码加入到消息框中的方法
    @staticmethod
    def send_target_key():
        target_key = Connection_program.target_key.get()
        # 判断是否为空
        if target_key == "":
            Connection_program.target_key.set("目标密钥不可为空！")
        elif target_key == "目标密钥不可为空！":
            pass
        else:
            # 改变相关状态
            Connection_program.key_distribution_exit_flag = False
            # 将数据加入到文本框中
            Connection_program.key_distribution_text.insert("end", "目标密钥:" + target_key + "\n")
            # 传送数据
            Connection_program.target_key_data = target_key
            # 改变状态
            Connection_program.target_key.set("您的密钥已就绪！")
            Connection_program.send_target_key_button.config(state="disabled")
            # 启动通信多线程
            th2 = threading.Thread(target=Connection_program.key_distribution)
            th2.start()

    # 由密钥分发模式回到通信界面的方法
    @staticmethod
    def key_distribution_mode_to_communication_interface():
        # 判断连接状态
        if Connection_program.key_distribution_exit_flag is True:
            Connection_program.key_distribution_frame.destroy()
            Connection_program.key_distribution_frame = None
            Connection_program.key_distribution_text = None
            Connection_program.target_key = None
            Main_program.communication_interface()

    # 密码分发模式的方法
    @staticmethod
    def key_distribution():
        Connection_program.key_distribution_exit_flag = False
        pass_flag1 = False
        pass_flag2 = False
        IP = Connection_program.get_host_ip()  # IPV4
        PORT = 6000  # 端口
        Max_data = Connection_program.Max_data  # 缓冲区大小
        # 设置网络层协议，传输层协议
        listenSocket = socket(AF_INET, SOCK_STREAM)  # IP协议，TCP
        listenSocket.bind((IP, PORT))  # 绑定地址和端口
        # 设置监听
        listenSocket.listen(1)  # 设置1个监听口
        # print('通信环境配置完毕，等待建立连接...')
        # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
        Connection_program.key_distribution_text.insert("end", "通信环境配置完毕，等待建立连接..." + "\n")
        # 设置连接
        dataSocket, addr = listenSocket.accept()  # 设置接收
        # print(f'成功连接来自{addr}的连接')
        # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
        Connection_program.key_distribution_text.insert("end", "成功连接来自:{}".format(addr) + "\n")
        dataSocket.send(File_program.user_sm2_public_key.encode())
        # print("系统已发送您的公钥:{}".format(File_program.user_sm2_public_key))
        # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
        Connection_program.key_distribution_text.insert("end", "系统已发送您的公钥:{}".format(File_program.user_sm2_public_key) + "\n")
        # 延时，防止粘包
        time.sleep(0.5)
        # 调用系统签名
        my_publickey_sign = SystemSign.system_sign_message(File_program.user_sm2_public_key)
        dataSocket.send(my_publickey_sign.encode())
        # print("系统已签名并发送您的公钥数据:{}".format(my_publickey_sign))
        # print("正在等待对方验证中...")
        # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
        Connection_program.key_distribution_text.insert("end", "系统已签名并发送您的公钥数据:{}".format(my_publickey_sign) + "\n")
        Connection_program.key_distribution_text.insert("end", "正在等待对方验证中..." + "\n")
        # 接收数据
        data = dataSocket.recv(Max_data)
        data = data.decode()
        # 判断是否通过验证
        if data == "pass":
            # print("\033[32m 已通过对方验证 \033[0m")
            # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
            Connection_program.key_distribution_text.insert("end", "已通过对方验证" + "\n")
            pass_flag1 = True
        else:
            # print("\033[91m 未通过对方验证！ \033[0m")
            # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
            Connection_program.key_distribution_text.insert("end", "未通过对方验证！" + "\n")
        # 接收对方发送的消息
        data = dataSocket.recv(Max_data)
        other_sm2_publickey = data.decode()
        # print("已接收到对方公钥:{}".format(other_sm2_publickey))
        # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
        Connection_program.key_distribution_text.insert("end", "已接收到对方公钥:{}".format(other_sm2_publickey) + "\n")
        data = dataSocket.recv(Max_data)
        other_sm2_publickey_sign = data.decode()
        # print("已接收对方签名公钥数据:{}".format(other_sm2_publickey_sign))
        # print("正在进行系统验证")
        # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
        Connection_program.key_distribution_text.insert("end", "已接收对方签名公钥数据:{}".format(other_sm2_publickey_sign) + "\n")
        Connection_program.key_distribution_text.insert("end", "正在进行系统验证" + "\n")
        if SystemVerify.system_verify_message(other_sm2_publickey_sign, other_sm2_publickey):
            # print("\033[32m 系统签名验证通过 \033[0m")
            # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
            Connection_program.key_distribution_text.insert("end", "系统验证通过" + "\n")
            pass_flag2 = True
            # 发送通过验证的消息
            dataSocket.send("pass".encode())
        else:
            # print("\033[91m 系统签名验证未通过，存在风险！\033[0m")
            # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
            Connection_program.key_distribution_text.insert("end", "系统签名验证未通过，存在风险！" + "\n")
            # 发送未通过验证的消息
            dataSocket.send("fail".encode())
        # 判断当前环境
        # print("正在判断当前密钥分发环境...")
        # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
        Connection_program.key_distribution_text.insert("end", "正在判断当前密钥分发环境..." + "\n")
        if pass_flag1 is False and pass_flag2 is False:
            # print("\033[91m当前状态无法进行密钥分发！\033[0m")
            # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
            Connection_program.key_distribution_text.insert("end", "当前状态无法进行密钥分发！" + "\n")
        elif (pass_flag1 is True and pass_flag2 is False) or (pass_flag1 is False and pass_flag2 is True):
            print("当前环境安全状态不佳")
            print("是继续否进行密钥分发？(1 继续执行， 其他 退出)")
            end_flag = input("输入:")
            if end_flag == "1":
                Connection_program.connect_flag = True
                print("您继续了执行密钥分发！")
                print("等待对方决定中")
                # 发送继续分发信号
                dataSocket.send("keep".encode())
                data = dataSocket.recv(Max_data)
                if data.decode() == "keep":
                    print("对方已同意，分发继续")
                    Connection_program.connect_flag = True
                else:
                    print("对方已拒绝，分发结束")
            else:
                print("您结束了密钥分发")
                # 发送结束分发信号
                dataSocket.send("finish".encode())
                dataSocket.recv(Max_data)
        else:
            # print("\033[32m当前环境安全，将继续进行密钥分发\033[0m")
            # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
            Connection_program.key_distribution_text.insert("end", "当前环境安全，将继续进行密钥分发" + "\n")
            Connection_program.connect_flag = True
        try:  # ------------------------------------------------------------------------------------------------------------
            # 判断是否继续通信
            if Connection_program.connect_flag is True:
                # 记录通信时的日期数据
                Connection_program.connect_time = time.strftime('%Y-%m-%d %H:%M:%S')
                # 调用创建自己公私钥加解密对象的方法
                my_sm2 = Crypto_program.create_my_sm2()
                # print("已创建己方sm2对象")
                # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
                Connection_program.key_distribution_text.insert("end", "创建己方sm2对象" + "\n")
                # 调用创建对方公钥加密对象的方法
                other_sm2 = Crypto_program.create_other_sm2(other_sm2_publickey)
                # print("已创建对方sm2对象")
                # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
                Connection_program.key_distribution_text.insert("end", "已创建对方sm2对象" + "\n")
                # 调用随机生成通信密钥
                Crypto_program.random_key_born()
                connect_key = Connection_program.connect_key_sm4
                # print("已生成随机通信密钥:{}".format(connect_key))
                # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
                Connection_program.key_distribution_text.insert("end", "已生成随机通信密钥:{}".format(connect_key) + "\n")
                # 使用对方的公钥加密通信密钥并发送
                en_connect_key = Crypto_program.sm2_publickey_encry(other_sm2, connect_key)
                dataSocket.send(en_connect_key.encode())  # 发送处理后的通信密钥
                # print('已发送使用对方公钥加密通信密钥后的数据:{}'.format(en_connect_key))
                # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
                Connection_program.key_distribution_text.insert("end", "已发送使用对方公钥加密通信密钥后的数字信封:{}".format(en_connect_key) + "\n")
                # 获得目标密钥
                target_key = Connection_program.target_key_data
                en_target_key = encrypt_ecb(target_key, connect_key)  # 使用 sm4 加密目标密钥
                dataSocket.send(en_target_key.encode())  # 发送目标密钥
                # print('已成功发送使用通信密钥加密后目标密钥数据:{}'.format(en_target_key))
                # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
                Connection_program.key_distribution_text.insert("end", "已成功发送使用通信密钥加密后的目标密钥数据:{}".format(en_target_key) + "\n")
                # 使用 sm3 哈希计算目标密钥
                target_key_hash = sm3.sm3_hash(func.bytes_to_list(target_key.encode()))
                # 将目标密钥哈希计算后使用自己的私钥进行签名
                sign_data = my_sm2.sign(target_key_hash.encode(), str(len(target_key)))
                print("已生成您对该目标密钥的签名数据:{}".format(sign_data))
                # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
                Connection_program.key_distribution_text.insert("end", "已生成您对该目标密钥的签名数据:{}".format(sign_data) + "\n")
                # 使用通信密钥加密消息
                en_sign_data = encrypt_ecb(sign_data, connect_key)
                time.sleep(0.3)  # 避免粘包
                dataSocket.send(en_sign_data.encode())  # 将签名后的消息发送
                # print("已发送使用通信密钥加密后的签名消息:{}".format(en_sign_data))
                # print("等待对方验签结果")
                # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
                Connection_program.key_distribution_text.insert("end", "已发送使用通信密钥加密后的签名消息:{}".format(en_sign_data) + "\n")
                Connection_program.key_distribution_text.insert("end", "等待对方验签结果" + "\n")
                accept_flag = dataSocket.recv(Max_data).decode()
                accept_flag = decrypt_ecb(accept_flag, connect_key)
                if accept_flag == "pass":
                    # print("\033[32m对方已成功验证了您的签名！\033[0m")
                    # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
                    Connection_program.key_distribution_text.insert("end", "对方成功验证了您的签名！" + "\n")
                    division_end = "对方已成功验证了您的签名！"
                else:
                    # print("\033[91m对方未能正确验证您的签名！\033[0m")
                    # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
                    Connection_program.key_distribution_text.insert("end", "对方未能正确验证您的签名！" + "\n")
                    division_end = "对方未能正确验证您的签名！"
                # 发送自己用户名
                dataSocket.send(encrypt_ecb(Main_program.user_name, connect_key).encode())
                # 接收对方用户名
                name = dataSocket.recv(Max_data).decode()
                name = decrypt_ecb(name, connect_key)
                # 初始化
                Connection_program.time_record_key_division_list.clear()
                # 记录此次密钥分发的情况
                Connection_program.time_record_key_division_list.append("对方公钥数据为:" + other_sm2_publickey + "\n")
                Connection_program.time_record_key_division_list.append("对方用户名为:" + name + "\n")
                Connection_program.time_record_key_division_list.append("自己的签名数据为:" + sign_data + "\n")
                if pass_flag1 is True:
                    sentence = "对方已通过验证，"
                else:
                    sentence = "对方未通过验证，"
                if pass_flag2 is True:
                    sentence = sentence + "已通过对方验证"
                else:
                    sentence = sentence + "未通过对方验证"
                Connection_program.time_record_key_division_list.append("分发的环境安全状况为:" + sentence + "\n")
                Connection_program.time_record_key_division_list.append("分发的密钥为:" + target_key + "\n")
                Connection_program.time_record_key_division_list.append("分发结果为:" + division_end + "\n")
                # 调用用户日志更新的方法(使用密钥分发模式)
                File_program.file_batch_encrypto(2)
        finally:
            listenSocket.close()  # 关闭监听
            dataSocket.close()  # 关闭通信
            # 改变通信状态
            Connection_program.connect_flag = False
            Connection_program.key_distribution_exit_flag = True
            # print("密钥分发已结束")
            # --------------------------------------------------------------------------------------------------------------数据加入到消息框中
            Connection_program.key_distribution_text.insert("end", "密钥分发已结束" + "\n")
            # 改变状态
            Connection_program.target_key.set("您的密钥已发送！")


# 定义文件处理类
class File_program(object):
    def __init__(self):
        super().__init__()
        pass

    """
    描述:
    加密方式:用户文件内容全部采用密码类中随机密钥生成的 16 位密钥进行SM4加密(为防止用户更改账户密码带来的大量的重新计算用户日志文件数据，以及加强用户日志文件的安全性)
    用户文件内容格式(前三行为固定格式)：
                  第一部分:  用户的日志文件加密密钥  (使用用户的账户密钥进行SM4加密，其余加密为常规)  (创建账户文件时应自动加入)
                  分段符号:   (不加密)
                  第二部分:  用户的公钥  (加密)  (创建账户文件时应自动加入)
                  分段符号:   (不加密)
                  第三部分:  用户的私钥  (加密)  (创建账户文件时应自动加入)
                  分段符号:   (不加密)
                    (加密)
                  (会话模式):
                           日期 + 空格 + 通信模式 + 空格 + 通信时长(单位秒) (独占一行，但和下面的数据一起加密)
                           括号内为自己发送的消息，括号外为接收到的消息  (加密)
                  分段符号:  (不加密)
                  (密钥分发模式):
                           日期 + 空格 + 通信模式 (独占一行，但和下面的数据一起加密)
                           对方公钥  (加密)
                           对方用户名  (加密)
                           对方的签名  (加密)
                           密钥分发时的环境状态  (加密)
                           分发的密钥  (加密)
                           分发结果  (加密)
                  分段符号:  (不加密)

    分段符号规定: @file_should_division + "\n"
    加密区域:两个分隔符号之间进行一次加密
    """
    # 以下数据变量的格式默认为 str 型
    # 用户公钥重复性检测文件
    test_sm2_public_key_file = "E:\密码学小学期20230821\python_socket通信\socket加密通信(服务端账户公钥文件).txt"
    # 处理的文件源文件夹
    root_file = "E:\密码学小学期20230821\python_socket通信"
    # 用户通信数据临时保存的变量
    temporary_data = ""
    # 用户文件内容存储变量
    user_file_data = ""
    # 用户的SM2公私钥对
    user_sm2_public_key = ""
    user_sm2_private_key = ""
    # 用户日志文件SM4加密密钥
    file_sm4_key = ""
    # 用户文件分段符号
    user_file_division = "@file_should_division" + "\n"
    # 用户日志文件路径名暂存变量
    user_logfile_name = ""
    # 用户日志文件明文暂存的列表
    user_logfile_plaint_content = []

    # 用户密钥及其他数据初始化的方法
    @staticmethod
    def user_filedata_init():
        file_name = "E:\密码学小学期20230821\python_socket通信" + "\\" + encrypt_ecb(Main_program.user_name, Main_program.user_password) + ".txt"
        # 记录用户日志文件名
        File_program.user_logfile_name = file_name
        # 以只读方式打开用户文件
        file = open(file_name, "r")
        file_list = file.readlines()
        file.close()
        # 临时数据
        data = ""
        data1 = ""
        data2 = ""
        data3 = ""
        # 读取用户日志文件，且只读取前三部分
        division_num = 0
        for line in file_list:
            # 判断是否为日志文件分段符
            if line != File_program.user_file_division:
                data = data + line
            else:
                # 判断为第几部分
                if division_num == 0:
                    # 安全的除去数据末"\n"的方法
                    data = data[0:len(data) - 1]
                    # 获得日志解密密钥(加密状态)
                    data1 = data
                elif division_num == 1:
                    # 安全的除去数据末"\n"的方法
                    data = data[0:len(data) - 1]
                    # 获得用户公钥(加密状态)
                    data2 = data
                elif division_num == 2:
                    # 安全的除去数据末"\n"的方法
                    data = data[0:len(data) - 1]
                    # 获得用户私钥(加密状态)
                    data3 = data
                    break
                # 临时数据重置
                data = ""
                # 分隔符记录加一
                division_num += 1
        # 使用用户账户密码解密获得用户日志SM4加密解密密钥
        File_program.file_sm4_key = decrypt_ecb(data1, Main_program.user_password)
        # 使用用户日志加解密密钥解密获得用户SM2公钥
        File_program.user_sm2_public_key = decrypt_ecb(data2, File_program.file_sm4_key)
        # 使用用户日志加解密密钥解密获得用户SM2私钥
        File_program.user_sm2_private_key = decrypt_ecb(data3, File_program.file_sm4_key)


    # 用户密钥及其他数据清除的方法
    @staticmethod
    def file_data_clear():
        # 清除三种密钥
        File_program.user_sm2_private_key = None
        File_program.user_sm2_public_key = None
        File_program.file_sm4_key = None
        # 格式化用户其他数据暂存变量
        File_program.user_logfile_name = None
        File_program.user_logfile_plaint_content.clear()

    # 创建用户日志文件的方法
    @staticmethod
    def create_register_file(name, password):
        path = "E:\密码学小学期20230821\python_socket通信"
        # 将用户文件名使用用户自己的账户密钥进行加密
        register_file_name = encrypt_ecb(name, password)
        # 创建账户文件
        full_path = path + "\\" + str(register_file_name) + ".txt"
        File_program.user_logfile_name = full_path
        user_file = open(File_program.user_logfile_name, "w")
        # 调用密钥随机生成函数
        Crypto_program.random_key_born()
        file_encry_key = Connection_program.connect_key_sm4
        # 写入日志文件加密密钥
        user_file.write(encrypt_ecb(file_encry_key, password) + "\n")
        # 分段符号
        user_file.write(File_program.user_file_division)
        while True:
            # 调用SM2公私钥对随机生成函数
            Crypto_program.sm2_key_random_born()
            # 读取指定文件中的数据
            file1 = open("E:\密码学小学期20230821\python_socket通信\pk_B.txt", "r")
            data1 = str(file1.read())
            # 写入覆盖文件
            # file1.write("")
            file1.close()
            # 调用公钥重复性检测的方法
            pass_flag1 = File_program.test_sm2_public_key(data1)
            # 调用检测公钥是否匹配的方法
            pass_flag2 = Crypto_program.test_sm2_key()
            # 检测返回值以判断公钥是否重复
            if pass_flag1 is True and pass_flag2 is True:
                break
            else:
                pass
        # 将生成的公钥加入到公钥文件中
        public_key_file = open(File_program.test_sm2_public_key_file, "a")
        public_key_file.write(data1 + "\n")
        public_key_file.close()
        # 写入已加密公钥
        user_file.write(encrypt_ecb(data1, file_encry_key) + "\n")
        # 分段符号
        user_file.write(File_program.user_file_division)
        # 读取指定文件中的数据
        file2 = open("E:\密码学小学期20230821\python_socket通信\sk_B.txt", "r")
        data2 = str(file2.read())
        # 写入覆盖文件
        # file2.write("")
        file2.close()
        # 写入已加密私钥
        user_file.write(encrypt_ecb(data2, file_encry_key) + "\n")
        # 分段符号
        user_file.write(File_program.user_file_division)
        # 关闭文件
        user_file.close()

    # 用户公钥重复性检测的方法
    @staticmethod
    def test_sm2_public_key(sm2_public_key):
        # 以只读模式打开用户公钥文件
        file = open(File_program.test_sm2_public_key_file, "r")
        # 读取公钥数据
        content = file.readlines()
        file.close()
        # 判断公钥是否重负
        if sm2_public_key in content:
            return False
        else:
            return True

    # 删除账户日志文件的方法
    @staticmethod
    def delete_register_file():
        # 删除该文件
        os.remove(File_program.user_logfile_name)

    # 用户日志文件加入更新加密日志数据的方法(1 会话模式， 2 密钥分发模式)
    @staticmethod
    def file_batch_encrypto(flag):
        # 先调用日志数据处理的方法
        File_program.connect_log_record(flag)
        # 以追加的模式打开用户日志文件
        file = open(File_program.user_logfile_name, "a")
        # 使用用户日志文件加密密钥SM4加密此次更新的数据
        update_data = encrypt_ecb(File_program.temporary_data, File_program.file_sm4_key)
        # 添加加密文件到用户文件中
        file.write(update_data + "\n")
        # 写入用户日志文件分段符号
        file.write(File_program.user_file_division)
        # 关闭文件
        file.close()

    # 用户更改账户密码时，更改日志文件中加解密密钥的加密状态
    @staticmethod
    def update_file_encry_key(old_key, new_key):
        # 判断新旧日志文件密钥加密密钥
        if old_key != new_key:
            # 计算日志密钥新加密的数据
            new_encry_file_sm4_key = encrypt_ecb(File_program.file_sm4_key, new_key)
            # 计算日志密钥旧加密方式的数据
            old_encry_file_sm4_key = encrypt_ecb(File_program.file_sm4_key, old_key)
            # 以只读的方式打开用户日志文件
            file = open(File_program.user_logfile_name, "r")
            content = file.read()
            file.close()
            # 替换数据
            content = content.replace(str(old_encry_file_sm4_key), str(new_encry_file_sm4_key))
            # 以重写的方式打开用户日志文件
            file = open(File_program.user_logfile_name, "w")
            file.write(content)
            file.close()
        else:
            pass

    # 用户日志文件读取及解密的方法
    @staticmethod
    def file_reading_decry():
        # 清空文件
        File_program.user_logfile_plaint_content = []
        # 用只读的方式打开用户文件
        file = open(File_program.user_logfile_name, "r")
        file_list = file.readlines()
        # encry_data_unit 为用户日志文件数据最小加解密单元
        # 记录日志文件分段符号个数
        division_num = 0
        # 临时数据
        data = ""
        # 从用户日志文件的第四行开始读入数据
        for line in file_list:
            # 判断是否为日志文件分段符号
            if line != File_program.user_file_division:
                if division_num >= 3:
                    data = data + line
            else:
                if division_num >= 3:
                    # 安全的除去文末"\n"的方法
                    data = data[0:len(data) - 1]
                    # 获得最小解密单元
                    encry_data_unit = data
                    # 解密
                    plaint_data = decrypt_ecb(encry_data_unit, File_program.file_sm4_key)
                    # 加入到用户日志明文暂存列表中
                    File_program.user_logfile_plaint_content.append(plaint_data)
                    # 临时数据重置
                    data = ""
                division_num += 1

    # 用户日志文件加密密钥修改的方法
    @staticmethod
    def update_file_key(old_key, new_key):
        if new_key != old_key:
            # 新加密日志数据暂存变量
            log_file_data = []
            # 调用用户日志文件读取及解密的方法
            File_program.file_reading_decry()
            # 使用新日志加密密钥加密数据
            for line in File_program.user_logfile_plaint_content:
                line = encrypt_ecb(line, new_key)
                log_file_data.append(line)
            # 以 重写模式 打开用户日志文件
            file = open(File_program.user_logfile_name, "w+")
            # 写入新的日志加解密密钥(已加密)
            file.write(encrypt_ecb(new_key, Main_program.user_password) + "\n")
            # 写入日志文件分段符号
            file.write(File_program.user_file_division)
            # 写入新加密的公钥数据
            file.write(encrypt_ecb(File_program.user_sm2_public_key, new_key) + "\n")
            # 写入日志文件分段符号
            file.write(File_program.user_file_division)
            # 写入新加密的私钥数据
            file.write(encrypt_ecb(File_program.user_sm2_private_key, new_key) + "\n")
            # 写入日志文件分段符号
            file.write(File_program.user_file_division)
            # 写入新加密的日志数据
            for line in log_file_data:
                file.write(line + "\n")
                # 写入日志文件分段符号
                file.write(File_program.user_file_division)
            # 清空
            log_file_data.clear()
        else:
            pass

    # 用户会话日志中转记录的方法(1 会话模式， 2 密钥分发模式)
    @staticmethod
    def connect_log_record(mode):
        # 清空数据
        File_program.temporary_data = ""
        # 判断传进参数的合法性
        if type(mode) == int:
            # 判断记录模式
            if mode == 1:
                # 会话模式
                # 先写入日志标准头
                File_program.temporary_data = "\n" + str(Connection_program.connect_time) + " " + "会话模式" + " " + str(Connection_program.connect_time_long) + "\n"
                # 再写入详细日志数据
                for line in Connection_program.time_record_data:
                    File_program.temporary_data = File_program.temporary_data + str(line)
            else:
                # 密钥分发模式
                # 先写入日志标准头
                File_program.temporary_data = "\n" + str(Connection_program.connect_time) + " " + "密钥分发模式" + "\n"
                # 再写入详细日志数据
                for line in Connection_program.time_record_key_division_list:
                    File_program.temporary_data = File_program.temporary_data + str(line)
        else:
            pass


# 定义主程序类--------------------------------------------------------------------------------------主要修改部分
class Main_program(object):
    def __init__(self):
        super().__init__()
        pass

    # 版本信息
    version = "2.10"
    # 系统文件状态
    text_content_flag = False
    loading_flag = False
    # 登录状态
    register_flag = False
    # 记录读取的账户文件信息(用于可能的账户信息修改，注销，重写于文件中)
    name_list = []
    password_list = []
    # 记录用户信息
    user_name = ""  # 用于后续的身份信息
    user_password = ""  # 用于后续的加密通讯
    # 各个窗口
    # 开始界面
    start_frame = None
    # 登录界面
    login_frame = None
    # 注册界面
    register_frame = None
    register_name = None
    register_password_one = None
    register_password_two = None
    # 登录页面
    login_name = None
    login_password = None
    try_login_time = 0
    # 用户界面
    user_frame = None
    # 用户菜单
    user_menu = None
    # 通信界面
    communication_frame = None
    # 日志界面
    log_frame = None
    # 修改日志数据界面
    edit_frame = None
    check_log_frame = None
    change_logfile_key_frame = None
    show_logfile_key = None
    # 修改用户数据界面
    change_user_frame = None
    change_user_name_frame = None
    change_user_name_data = None
    change_user_password_frame = None
    change_user_password_old_data = None
    change_user_password_new_data1 = None
    change_user_password_new_data2 = None
    # 用户注销界面
    register_logout_frame = None
    verify_password = None

    # 清空当前屏幕的方法
    @staticmethod
    def clear_screen():
        pyautogui.hotkey("Ctrl", "Shift", "Tab")

    # 账户文件加载的方法
    @staticmethod
    def file_loading():
        flag = False
        # 对文件进行判断
        way = r"E:\密码学小学期20230821\python_socket通信\socket加密通信(服务端账户文件).txt"
        if os.path.exists(way):
            content = os.path.getsize(way)
            if not content:
                Main_program.text_content_flag = False
            else:
                Main_program.text_content_flag = True
            flag = True
        else:
            # print("\033[91m账户文件丢失，加载失败!\033[0m")
            Label(root_window, text="账户文件加载失败！", font=("黑体", 15), fg="red").pack()
        # 判断读取文件的标志
        if flag is True:
            # 清空账户信息列表
            Main_program.name_list.clear()
            Main_program.password_list.clear()
            # 打开并读取账户文件(只读模式)
            files = open(r"E:\密码学小学期20230821\python_socket通信\socket加密通信(服务端账户文件).txt", "r")
            for line in files.readlines():
                # 读取特定字段
                Main_program.name_list.append(line[0:64])
                Main_program.password_list.append(line[65:129])
            # 关闭文件
            files.close()
            # 修改文件加载状态
            Main_program.loading_flag = True
        else:
            pass

    # 验证登录的方法(登录界面)
    @staticmethod
    def login_detection():
        # 创建登录界面
        Main_program.login_frame = Frame(root_window, padx=70, pady=50, relief="raised")
        Main_program.login_frame.place(anchor="center", relx=0.5, rely=0.5)
        # 判断账户文件是否为空
        if Main_program.text_content_flag is True:
            # 初始化数据
            Main_program.login_name = StringVar()
            Main_program.login_password = StringVar()
            # 提示语
            Label(Main_program.login_frame, text="请输入账户名与密码登录", font=("kaiti", 18)).pack()
            # 账户名
            Label(Main_program.login_frame, text="用户名", font=("黑体", 15),).pack()
            Entry(Main_program.login_frame, font=("黑体", 15), textvariable=Main_program.login_name).pack()
            # 账户密码
            Label(Main_program.login_frame, text="用户密码", font=("黑体", 15),).pack()
            Entry(Main_program.login_frame, font=("黑体", 15), show="*", textvariable=Main_program.login_password).pack()
            # 登录
            Button(Main_program.login_frame, text="登录", font=("黑体", 15), fg="red", command=Main_program.login_detection_to_user_interface).pack(side="left")
            # 取消
            Button(Main_program.login_frame, text="取消", font=("黑体", 15), command=Main_program.login_detection_to_start).pack(side="right")
            if Main_program.try_login_time > 5:
                Label(Main_program.login_frame, text="您已错误输入超过 5 次！", font=("kaiti", 10), fg="red").pack(side="bottom")
        else:
            Label(Main_program.login_frame, text="抱歉，账户文件中为空，无法登陆！", font=("kaiti", 18), fg="red").pack()
            Button(Main_program.login_frame, text="点击返回", font=("kaiti", 15), fg="blue", command=Main_program.login_detection_to_start).pack()

    # 登录转向用户界面的方法
    @staticmethod
    def login_detection_to_user_interface():
        pass_flag1 = False
        pass_flag2 = False
        name = Main_program.login_name.get()
        password = Main_program.login_password.get()
        # 检测用户名是否存在
        mid_name = sm3.sm3_hash(func.bytes_to_list(name.encode()))
        for i in range(len(Main_program.name_list)):
            # 遍历搜索检测账户名
            if Main_program.name_list[i] == mid_name:
                pass_flag1 = True
                # 检测密码是否正确
                mid_password = sm3.sm3_hash(func.bytes_to_list(password.encode()))
                if Main_program.password_list[i] == mid_password:
                    pass_flag2 = True
        if pass_flag1 is False:
            Main_program.login_name.set('不存在该用户！')
        if pass_flag2 is False:
            Main_program.login_password.set("密码错误！")
        # 检测是否通过
        if pass_flag1 is True and pass_flag2 is True:
            Main_program.try_login_time = 0
            Main_program.user_name = name
            Main_program.user_password = password
            Main_program.login_frame.destroy()
            Main_program.user_interface()
        else:
            if Main_program.try_login_time > 5:
                Main_program.login_detection_to_start()
            else:
                Main_program.try_login_time += 1

    # 登录转向开始菜单的方法
    @staticmethod
    def login_detection_to_start():
        Main_program.login_frame.destroy()
        Main_program.start()

    # 注册账户的方法(注册界面)
    @staticmethod
    def register_account():
        # 初始化数据
        Main_program.register_name = StringVar()
        Main_program.register_password_one = StringVar()
        Main_program.register_password_two = StringVar()
        # 创建注册的容器
        Main_program.register_frame = Frame(root_window, width=400, height=200, padx=70, pady=50, relief="raised")
        Main_program.register_frame.place(anchor="center", relx=0.5, rely=0.5)
        # 提示语
        Label(Main_program.register_frame, text="请输入数据以注册", font=("kaiti", 18)).pack()
        # 用户名
        Label(Main_program.register_frame, text="账户名:", font=("黑体", 15), padx=10, pady=10).pack(anchor="center")
        Entry(Main_program.register_frame, textvariable=Main_program.register_name, font=("黑体", 13), width=23).pack()
        # 密码
        Label(Main_program.register_frame, text="密码:", font=("黑体", 15), padx=10, pady=10).pack(anchor="center")
        Entry(Main_program.register_frame, textvariable=Main_program.register_password_one, font=("黑体", 13), width=23).pack(anchor="center")
        Label(Main_program.register_frame, text="再次输入密码:", font=("黑体", 15), padx=10, pady=10).pack(anchor="center")
        Entry(Main_program.register_frame, textvariable=Main_program.register_password_two, font=("黑体", 13), width=23).pack(anchor="center")
        # 注册
        Button(Main_program.register_frame, text="注册", font=("黑体", 15), relief="raised", fg="red", padx=5, pady=5, command=Main_program.register_account_data_test).pack(anchor="center", side="left")
        # 取消
        Button(Main_program.register_frame, text="取消", font=("黑体", 15), relief="raised", fg="black", padx=5, pady=5, command=Main_program.register_account_to_start).pack(anchor="center", side="right")

    # 注册转向开始菜单的方法
    @staticmethod
    def register_account_data_test():
        pass_flag = True
        # 判断输入的合法性
        # 检测用户名的合法性
        name = Main_program.register_name.get()
        if name == "":
            Main_program.register_name.set("用户名不能为空！")
            pass_flag = False
        elif len(name) > 12:
            Main_program.register_name.set("用户名不能过长！")
            pass_flag = False
        mid_name = sm3.sm3_hash(func.bytes_to_list(name.encode()))
        for name_test in Main_program.name_list:  # 判断是否重名
            # 检测是否有重名用户
            if mid_name == name_test:
                Main_program.register_name.set("该用户名已存在！")
                pass_flag = False
                break
        # 检测密码的合法性
        password1 = Main_program.register_password_one.get()
        password2 = Main_program.register_password_two.get()
        # 调用密钥强度检测的方法
        if Crypto_program.test_key_strong(password1, 16):
            pass
        else:
            Main_program.register_password_one.set("密钥强度不够或不合法！")
            pass_flag = False
        # 检测两次输入密码是否一致
        if password1 != password2:
            Main_program.register_password_two.set("两次输入的密钥不一致！")
            pass_flag = False
        # 检测所有检测是否通过，否者重新输入数据
        if pass_flag is True:
            Main_program.clear_frame(Main_program.register_frame)
            Label(Main_program.register_frame, text="恭喜注册成功，为您创建数据中，这可能需要花费几秒...").pack()
            # 打开账户文件 (追加模式)
            files = open(r"E:\密码学小学期20230821\python_socket通信\socket加密通信(服务端账户文件).txt", "a")
            mid_password = sm3.sm3_hash(func.bytes_to_list(password1.encode()))
            mid_name = mid_name + " "
            mid_password = mid_password + "\n"
            # 写入账户文件中
            files.write(mid_name)
            files.write(mid_password)
            # 调用用户日志文件创建的方法
            File_program.create_register_file(name, password1)
            files.close()
            # 修改文件加载状态
            Main_program.loading_flag = False
            # 完成提示语
            Label(Main_program.register_frame, text="完成！").pack()
            Main_program.register_frame.destroy()
            Main_program.start()

    # 注册界面取消的方法
    @staticmethod
    def register_account_to_start():
        Main_program.register_frame.destroy()
        Main_program.start()

    # 启动用户界面的方法(用户界面)
    @staticmethod
    def user_interface():
        # 加载用户数据
        File_program.user_filedata_init()
        # 创建用户菜单
        Main_program.user_frame = Frame(root_window, padx=40, pady=20)
        Main_program.user_frame.place(anchor="center", relx=0.5, rely=0.5,)
        Label(Main_program.user_frame, text="欢迎来到用户界面:{}".format(Main_program.user_name), font=("kaiti", 15)).grid(row=1)
        # 通信
        Button(Main_program.user_frame, text="进行通信", font=("黑体", 15), fg="blue", pady=10, command=Main_program.user_interface_to_communication_interface).grid(row=3)
        # 日志
        Button(Main_program.user_frame, text="日志数据", font=("黑体", 15), fg="blue", pady=10, command=Main_program.user_interface_to_log).grid(row=5)
        # 修改
        Button(Main_program.user_frame, text="修改账户", font=("黑体", 15), fg="blue", pady=10, command=Main_program.user_interface_to_register_edit).grid(row=7)
        # 注销
        Button(Main_program.user_frame, text="注销账户", font=("黑体", 15), fg="blue", pady=10, command=Main_program.user_interface_to_register_logout).grid(row=9)
        # 退出
        Button(Main_program.user_frame, text="退出登录", font=("黑体", 15), fg="red", pady=10, command=Main_program.user_interface_to_start).grid(row=11)

    # 由用户界面到通信界面的方法
    @staticmethod
    def user_interface_to_communication_interface():
        Main_program.user_frame.destroy()
        Main_program.communication_interface()

    # 通信的方法(通信界面)
    @staticmethod
    def communication_interface():
        # 创建通信界面
        Main_program.communication_frame = Frame(root_window, padx=40, pady=20, width=400, height=300)
        Main_program.communication_frame.place(anchor="center", relx=0.5, rely=0.5,)
        # 提示语
        Label(Main_program.communication_frame, text="欢迎来到通信界面:{}".format(Main_program.user_name), font=("kaiti", 15)).grid(row=1)
        # 会话模式
        Button(Main_program.communication_frame, text="会话模式", font=("黑体", 15), fg="blue", pady=10, command=Main_program.communication_interface_to_conversation_mode).grid(row=3)
        # 密钥分发模式
        Button(Main_program.communication_frame, text="密钥分发模式", font=("黑体", 15), fg="blue", pady=10, command=Main_program.communication_interface_to_key_distribution_mode).grid(row=5)
        # 退出
        Button(Main_program.communication_frame, text="退出", font=("黑体", 15), fg="red", pady=10, command=Main_program.communication_interface_to_user_interface).grid(row=7)

    # 从通信界面到会话模式的方法
    @staticmethod
    def communication_interface_to_conversation_mode():
        Main_program.communication_frame.destroy()
        Connection_program.init_communication()

    # 从通信界面到密钥分发模式的方法
    @staticmethod
    def communication_interface_to_key_distribution_mode():
        Main_program.communication_frame.destroy()
        Connection_program.init_key_distribution()

    # 从通信界面退出到用户界面的方法
    @staticmethod
    def communication_interface_to_user_interface():
        Main_program.communication_frame.destroy()
        Main_program.user_interface()

    # 从用户界面到日志界面的方法
    @staticmethod
    def user_interface_to_log():
        Main_program.user_frame.destroy()
        Main_program.register_log_interface()

    # 用户日志操作的方法(账户日志界面)
    @staticmethod
    def register_log_interface():
        # 创建修改界面
        Main_program.log_frame = Frame(root_window, padx=40, pady=20)
        Main_program.log_frame.place(anchor="center", relx=0.5, rely=0.5,)
        Label(Main_program.log_frame, text="欢迎来到日志界面:{}".format(Main_program.user_name), font=("kaiti", 15)).pack()
        # 查看日志
        Button(Main_program.log_frame, text="查看日志", font=("黑体", 15), fg="blue", pady=10, command=Main_program.check_log).pack()
        # 修改日志加密密钥
        Button(Main_program.log_frame, text="修改日志加密密钥", font=("黑体", 15), fg="blue", pady=10, command=Main_program.change_logfile_key).pack()
        # 退出
        Button(Main_program.log_frame, text="退出", font=("黑体", 15), fg="red", pady=10, command=Main_program.log_interface_to_user_interface).pack()

    # 查看日志的方法
    @staticmethod
    def check_log():
        log_data = ""
        Main_program.log_frame.destroy()
        # 用户日志文件预加载处理
        File_program.user_filedata_init()
        # 调用日志文件读取的方法
        File_program.file_reading_decry()
        # 以行展示用户通信日志数据
        for line in File_program.user_logfile_plaint_content:
            log_data = log_data + line
        # 创建显示日志数据的数据框
        Main_program.check_log_frame = Frame(root_window, padx=40, pady=20)
        Main_program.check_log_frame.place(anchor="center", relx=0.5, rely=0.5,)
        log_text = Text(Main_program.check_log_frame, width=100, height=40)
        log_text.pack(anchor="center",)
        log_text.insert("end", log_data)
        log_text.config(state="disabled")
        # 将滚动条设置在容器右边并填充全部y轴
        Scrollbar(Main_program.check_log_frame, command=log_text.yview).pack(side="right", fill="y")
        Button(Main_program.check_log_frame, text="退出", font=("黑体", 15), fg="red", command=Main_program.back_to_log_frame1).pack()

    # 返回日志主界面的方法
    @staticmethod
    def back_to_log_frame1():
        Main_program.check_log_frame.destroy()
        Main_program.register_log_interface()

    # 修改日志数据加密密钥的的方法
    @staticmethod
    def change_logfile_key():
        # 用户日志文件预加载处理
        File_program.user_filedata_init()
        Main_program.log_frame.destroy()
        # 创建所需的容器
        Main_program.change_logfile_key_frame = Frame(root_window, padx=40, pady=20)
        Main_program.change_logfile_key_frame.place(anchor="center", relx=0.5, rely=0.5,)
        # 显示当前用于加密日志数据的密钥
        Label(Main_program.change_logfile_key_frame, text="当前用于加密日志数据的密钥为:", font=("黑体", 15)).pack()
        Main_program.show_logfile_key = Text(Main_program.change_logfile_key_frame, width=20, height=2, state="disabled", font=("黑体", 15))
        Main_program.show_logfile_key.pack()
        # 更改密钥的按钮
        Button(Main_program.change_logfile_key_frame, text="更改密钥", font=("黑体", 15), fg="blue", command=Main_program.change_key).pack(side="left")
        # 退出的按钮
        Button(Main_program.change_logfile_key_frame, text="退出", font=("黑体", 15), fg="red", command=Main_program.back_to_log_frame2).pack(side="right")

    # 更改日志加密密钥的方法
    @staticmethod
    def change_key():
        # 调用随机密钥生成的方法
        Crypto_program.random_key_born()
        # 获取新密钥
        new_key = Connection_program.connect_key_sm4
        Main_program.show_logfile_key.config(state="normal")
        Main_program.show_logfile_key.delete("1.0", "end")
        Main_program.show_logfile_key.insert("end", new_key)
        Main_program.show_logfile_key.config(state="disabled")
        # 调用更新用户日志文件加密密钥的方法
        File_program.update_file_key(File_program.file_sm4_key, new_key)
        File_program.file_sm4_key = new_key

    # 返回日志界面
    @staticmethod
    def back_to_log_frame2():
        Main_program.change_logfile_key_frame.destroy()
        Main_program.register_log_interface()

    # 从日志界面返回用户界面的方法
    @staticmethod
    def log_interface_to_user_interface():
        Main_program.log_frame.destroy()
        Main_program.user_interface()

    # 从用户界面到账户修改界面的方法
    @staticmethod
    def user_interface_to_register_edit():
        Main_program.user_frame.destroy()
        Main_program.register_edit()

    # 账户修改的方法(账户修改界面)
    @staticmethod
    def register_edit():
        # 创建账户修改界面
        Main_program.change_user_frame = Frame(root_window, padx=40, pady=20)
        Main_program.change_user_frame.place(anchor="center", relx=0.5, rely=0.5,)
        Label(Main_program.change_user_frame, text="欢迎来到用户修改界面:{}".format(Main_program.user_name), font=("kaiti", 15)).pack()
        # 修改账户名按钮
        Button(Main_program.change_user_frame, text="修改账户名", font=("黑体", 15), fg="blue", pady=10, command=Main_program.change_user_name).pack()
        # 修改用户密码按钮
        Button(Main_program.change_user_frame, text="修改账户密码", font=("黑体", 15), fg="blue", pady=10, command=Main_program.change_user_password).pack()
        # 退出按钮
        Button(Main_program.change_user_frame, text="退出", font=("黑体", 15), fg="red", pady=10, command=Main_program.register_edit_to_user_interface).pack()

    # 修改用户名的方法(界面)
    @staticmethod
    def change_user_name():
        Main_program.change_user_name_data = StringVar()
        Main_program.change_user_frame.destroy()
        # 创建界面
        Main_program.change_user_name_frame = Frame(root_window, padx=40, pady=20)
        Main_program.change_user_name_frame.place(anchor="center", relx=0.5, rely=0.5,)
        # 修改界面
        Label(Main_program.change_user_name_frame, text="修改用户名:", font=("黑体", 15),).pack()
        Entry(Main_program.change_user_name_frame, textvariable=Main_program.change_user_name_data, font=("黑体", 15),).pack()
        # 确定按钮
        Button(Main_program.change_user_name_frame, text="修改", font=("黑体", 15), command=Main_program.change_user_name_base).pack(side="left")
        # 退出按钮
        Button(Main_program.change_user_name_frame, text="退出", font=("黑体", 15), fg="red", command=Main_program.back_to_register_edit1).pack(side="right")

    # 修改用户名的方法
    @staticmethod
    def change_user_name_base():
        pass_flag = True
        new_name = Main_program.change_user_name_data.get()
        # 获取哈希值
        new_mid_name = sm3.sm3_hash(func.bytes_to_list(new_name.encode()))
        # 遍历用户数据表
        for name_test in Main_program.name_list:
            # 检测是否有重名用户
            if new_mid_name == name_test:
                Main_program.change_user_name_data.set("已存在该用户名！")
                pass_flag = False
        # 判断是否可以修改至该用户名
        if pass_flag is True:
            old_mid_name = sm3.sm3_hash(func.bytes_to_list(Main_program.user_name.encode()))
            # 修改用户文件名
            old_file_name = "E:\密码学小学期20230821\python_socket通信" + "\\" + str(
                encrypt_ecb(Main_program.user_name, Main_program.user_password)) + ".txt"
            new_file_name = "E:\密码学小学期20230821\python_socket通信" + "\\" + str(
                encrypt_ecb(new_name, Main_program.user_password)) + ".txt"
            os.rename(old_file_name, new_file_name)
            # 遍历账户信息列表进行修改
            for i in range(len(Main_program.name_list)):
                # 判断是否为同一用户名
                if Main_program.name_list[i] == old_mid_name:
                    # 修改账户名
                    Main_program.name_list[i] = new_mid_name
                    Main_program.user_name = new_name
            Label(Main_program.change_user_name_frame, text="修改用户名成功！", font=("黑体", 15), fg="green").pack()
            # 重写文件
            files = open(r"E:\密码学小学期20230821\python_socket通信\socket加密通信(服务端账户文件).txt", "a+")
            # 清空文件
            files.truncate(0)
            files.close()
            # 以追加模式再次打开文件
            files = open(r"E:\密码学小学期20230821\python_socket通信\socket加密通信(服务端账户文件).txt", "a")
            for i in range(len(Main_program.name_list)):
                name = Main_program.name_list[i] + " "
                password = Main_program.password_list[i] + "\n"
                files.write(name)
                files.write(password)
            # 关闭文件
            files.close()

    # 回到账户修改界面的方法
    @staticmethod
    def back_to_register_edit1():
        Main_program.change_user_name_frame.destroy()
        Main_program.register_edit()

    # 修改用户密码的方法(界面)
    @staticmethod
    def change_user_password():
        # 初始化变量数据
        Main_program.change_user_password_old_data = StringVar()
        Main_program.change_user_password_new_data1 = StringVar()
        Main_program.change_user_password_new_data2 = StringVar()
        Main_program.change_user_frame.destroy()
        # 创建修改密码界面
        Main_program.change_user_password_frame = Frame(root_window, padx=40, pady=20)
        Main_program.change_user_password_frame.place(anchor="center", relx=0.5, rely=0.5,)
        # 旧密码验证
        Label(Main_program.change_user_password_frame, text="旧密码:", font=("黑体", 15), ).pack()
        Entry(Main_program.change_user_password_frame, font=("黑体", 15), width=23, textvariable=Main_program.change_user_password_old_data).pack()
        # 新密码输入
        Label(Main_program.change_user_password_frame, text="新密码:", font=("黑体", 15),).pack()
        Entry(Main_program.change_user_password_frame, font=("黑体", 15), width=23, textvariable=Main_program.change_user_password_new_data1).pack()
        # 新密码再次输入
        Label(Main_program.change_user_password_frame, text="再次确定新密码:", font=("黑体", 15), ).pack()
        Entry(Main_program.change_user_password_frame, font=("黑体", 15), width=23, textvariable=Main_program.change_user_password_new_data2).pack()
        # 确定按钮
        Button(Main_program.change_user_password_frame, text="修改", font=("黑体", 15), command=Main_program.change_user_password_base).pack(side="left")
        # 退出按钮
        Button(Main_program.change_user_password_frame, text="退出", font=("黑体", 15), fg="red", command=Main_program.back_to_register_edit2).pack(side="right")

    # 修改用户密码的方法
    @staticmethod
    def change_user_password_base():
        pass_flag = True
        old_password = Main_program.change_user_password_old_data.get()
        new_password1 = Main_program.change_user_password_new_data1.get()
        new_password2 = Main_program.change_user_password_new_data2.get()
        # 验证旧密码
        if old_password != Main_program.user_password:
            Main_program.change_user_password_old_data.set("旧密码错误！")
        else:
            # 新密码密码强度检测
            if Crypto_program.test_key_strong(new_password1, 16):
                pass
            else:
                Main_program.change_user_password_new_data1.set("密码强度过低或不合法！")
                pass_flag = False
            # 检测两次密码输入是否一致
            if new_password1 != new_password2:
                Main_program.change_user_password_new_data2.set("密码不一致！")
                pass_flag = False
            # 判断是否可以修改密码
            if pass_flag is True:
                old_mid_password = sm3.sm3_hash(func.bytes_to_list(old_password.encode()))
                for i in range(len(Main_program.password_list)):
                    # 判断是否为同一密码
                    if Main_program.password_list[i] == old_mid_password:
                        # 修改密码
                        Main_program.password_list[i] = sm3.sm3_hash(func.bytes_to_list(new_password1.encode()))
                        # 组合产生新的用户日志文件
                        new_user_logfile_name = File_program.root_file + "\\" + str(
                            encrypt_ecb(Main_program.user_name, new_password1)) + ".txt"
                        # 修改用户日志文件名
                        os.rename(File_program.user_logfile_name, new_user_logfile_name)
                        File_program.user_logfile_name = new_user_logfile_name
                        # 调用用户密码改变时日志文件密钥加密状态更改的方法
                        File_program.update_file_encry_key(old_password, new_password1)
                        Main_program.user_password = new_password1
                # 重写文件
                files = open(r"E:\密码学小学期20230821\python_socket通信\socket加密通信(服务端账户文件).txt", "a+")
                # 清空文件
                files.truncate(0)
                files.close()
                # 以追加模式再次打开文件
                files = open(r"E:\密码学小学期20230821\python_socket通信\socket加密通信(服务端账户文件).txt", "a")
                for i in range(len(Main_program.name_list)):
                    name = Main_program.name_list[i] + " "
                    password = Main_program.password_list[i] + "\n"
                    files.write(name)
                    files.write(password)
                # 关闭文件
                files.close()

    # 回到账户修改界面的方法
    @staticmethod
    def back_to_register_edit2():
        Main_program.change_user_password_frame.destroy()
        Main_program.register_edit()

    # 从账户修改界面返回用户界面的方法
    @staticmethod
    def register_edit_to_user_interface():
        Main_program.change_user_frame.destroy()
        Main_program.user_interface()

    # 从用户界面到账户注销界面的方法
    @staticmethod
    def user_interface_to_register_logout():
        Main_program.user_frame.destroy()
        Main_program.register_logout()

    # 账户注销的方法(账户注销界面)
    @staticmethod
    def register_logout():
        # 初始化变量数据
        Main_program.verify_password = StringVar()
        # 创建账户注销界面
        Main_program.register_logout_frame = Frame(root_window, padx=40, pady=20)
        Main_program.register_logout_frame.place(anchor="center", relx=0.5, rely=0.5,)
        Label(Main_program.register_logout_frame, text="您已来到账户注销界面！", font=("黑体", 15), fg="red").pack()
        # 旧密码验证
        Label(Main_program.register_logout_frame, text="请输入账户密码验证:", font=("黑体", 15), ).pack()
        Entry(Main_program.register_logout_frame, font=("黑体", 15), width=23, textvariable=Main_program.verify_password).pack()
        # 注销按钮
        Button(Main_program.register_logout_frame, text="注销", font=("黑体", 15), width=23, fg="red", command=Main_program.delete_user_data).pack(side="left")
        # 取消按钮
        Button(Main_program.register_logout_frame, text="取消", font=("黑体", 15), width=23, fg="blue", command=Main_program.register_logout_to_user_interface).pack(side="right")

    # 注销账户的方法
    @staticmethod
    def delete_user_data():
        password = Main_program.verify_password.get()
        # 验证密码是否一致
        if password != Main_program.user_password:
            Main_program.verify_password.set("验证失败！")
        else:
            pass
            # 弹出警告窗
            messagebox.showwarning("警告", "您已注销账户！")
            mid_name = sm3.sm3_hash(func.bytes_to_list(Main_program.user_name.encode()))
            for i in range(len(Main_program.name_list)):
                # 搜索目标用户
                if Main_program.name_list[i] == mid_name:
                    # 从账户信息列表中移除
                    Main_program.name_list.remove(Main_program.name_list[i])
                    Main_program.password_list.remove(Main_program.password_list[i])
                    break
            # 重写文件
            files = open(r"E:\密码学小学期20230821\python_socket通信\socket加密通信(服务端账户文件).txt", "a+")
            # 清空文件
            files.truncate(0)
            files.close()
            # 以追加模式再次打开文件
            files = open(r"E:\密码学小学期20230821\python_socket通信\socket加密通信(服务端账户文件).txt", "a")
            for i in range(len(Main_program.name_list)):
                name = Main_program.name_list[i] + " "
                password = Main_program.password_list[i] + "\n"
                files.write(name + password)
            # 关闭文件
            files.close()
            # 调用用户日志文件删除的方法
            File_program.delete_register_file()
            # 修改登录状态
            Main_program.register_flag = False
            # 修改文件加载状态
            Main_program.loading_flag = False
            # 调用返回开始菜单界面的方法
            Main_program.register_logout_to_start()

    # 注销账户取消回到用户界面的方法
    @staticmethod
    def register_logout_to_user_interface():
        Main_program.register_logout_frame.destroy()
        Main_program.user_interface()

    # 注销账户成功返回开始菜单的方法
    @staticmethod
    def register_logout_to_start():
        Main_program.register_logout_frame.destroy()
        Main_program.start()

    # 退出登录进入开始菜单的方法
    @staticmethod
    def user_interface_to_start():
        Main_program.user_frame.destroy()
        # 调用用户日志文件密钥及其他数据清除的方法
        File_program.file_data_clear()
        Main_program.start()

    # 关闭程序的方法
    @staticmethod
    def close_program():
        # 清屏
        Main_program.clear_screen()
        root_window.destroy()
        exit()

    # 启动程序的方法(主菜单界面)
    @staticmethod
    def start():
        # 主菜单界面容器
        Main_program.start_frame = Frame(root_window, relief="raised")
        Main_program.start_frame.place(anchor="center", relx=0.5, rely=0.5)
        # 判断文件是否已加载
        if Main_program.loading_flag is False:
            # 调用文件加载的方法
            Main_program.file_loading()
        else:
            pass
        # 开启时的显示语
        Label(Main_program.start_frame, text="欢迎使用基于socket的密钥分发系统", font=("kaiti", 18), padx=20, pady=20, anchor="center").pack()
        Label(Main_program.start_frame, text="请选择操作:",  font=("kaiti", 18), padx=20, pady=25, anchor="center").pack()
        # 登录按钮
        Button(Main_program.start_frame, text="登录", fg="blue", font=("黑体", 15), anchor="center", command=Main_program.start_to_register_detection).pack()
        # 注册按钮
        Button(Main_program.start_frame, text="注册", fg="blue", font=("黑体", 15), anchor="center", command=Main_program.start_to_register_account).pack()
        # 退出按钮
        Button(Main_program.start_frame, text="退出", fg="red", font=('黑体', 15), anchor="center", command=Main_program.close_program).pack()

    # 开始菜单转向各个界面的方法
    @staticmethod
    def start_to_register_detection():  # 转向登录界面
        Main_program.start_frame.destroy()
        Main_program.login_detection()

    @staticmethod
    def start_to_register_account():  # 转向注册界面
        Main_program.start_frame.destroy()
        Main_program.register_account()

    # 清除容器内组件组件的方法
    @staticmethod
    def clear_frame(frame):
        for item in frame.winfo_children():
            item.destroy()


# 创建工具栏类
class Main_menu(object):
    def __init__(self):
        super().__init__()

    # 创建工具栏的方法
    @staticmethod
    def create_menu():
        # 创建工具栏
        root_menu = Menu(root_window, )
        root_window.config(menu=root_menu)
        root_menu.add_command(label="帮助", command=Main_menu.help_function)
        root_menu.add_command(label="关于", command=Main_menu.about_function)
        root_menu.add_command(label="其他", command=Main_menu.other_function)

    # 以下均为实现工具栏中的方法
    @staticmethod
    def help_function():
        messagebox.showinfo("帮助", "此版本仅仅完成了基本功能，若遇到以下问题: \n 1 退出按键失效：使用任务管理器将其关闭 \n 2 通信失败；请确保通信双方使用同一对系统，同一模式下 \n 3 日志查看失败：您的日志文件可能丢失或损坏")

    @staticmethod
    def about_function():
        messagebox.showinfo("关于", "这是socket加密通信(服务端独立界面版2.10)，是初代版本 \n 作者: 信安21-1 任磊 \n 完成日期: 2023-9-3")

    @staticmethod
    def other_function():
        messagebox.showinfo("其他", "此版本仅完成了基本功能，尚未完善")


# 避免用户非正常退出程序的函数
def deny_exit():
    messagebox.showwarning("警告", "您无法从此处退出程序",)

# 创建根窗口
root_window = Tk()
root_window.title("socket密钥分发系统(服务端独立界面版{})".format(Main_program.version))
root_window.geometry("1600x1200+400+200")
root_window.config(bg="azure")
# root_window.resizable(False, False)
root_window.protocol("WM_DELETE_WINDOW", deny_exit)

# 调用创建工具栏的方法
Main_menu.create_menu()

# 启动程序
Main_program.start()

# 使窗口持续展示
root_window.mainloop()
